# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: site-packages\pystache\parser.py
"""
Exposes a parse() function to parse template strings.

"""
import re
from pystache import defaults
from pystache.parsed import ParsedTemplate

END_OF_LINE_CHARACTERS = ["\r", "\n"]
NON_BLANK_RE = re.compile("^(.)", re.M)


def parse(template, delimiters=None):
    """
    Parse a unicode template string and return a ParsedTemplate instance.
    
    Arguments:
    
      template: a unicode template string.
    
      delimiters: a 2-tuple of delimiters.  Defaults to the package default.
    
    Examples:
    
    >>> parsed = parse("Hey {{#who}}{{name}}!{{/who}}")
    >>> print(str(parsed).replace('u', ''))  # This is a hack to get the test to pass both in Python 2 and 3.
    ['Hey ', _SectionNode(key='who', index_begin=12, index_end=21, parsed=[_EscapeNode(key='name'), '!'])]
    
    """
    if type(template) is not str:
        raise Exception("Template is not unicode: %s" % type(template))
    parser = _Parser(delimiters)
    return parser.parse(template)


def _compile_template_re(delimiters):
    """
    Return a regular expresssion object (re.RegexObject) instance.
    
    """
    tag_types = "!>&/#^"
    tag = (
        "\n        (?P<whitespace>[\\ \\t]*)\n        %(otag)s \\s*\n        (?:\n          (?P<change>=) \\s* (?P<delims>.+?)   \\s* = |\n          (?P<raw>{)    \\s* (?P<raw_name>.+?) \\s* } |\n          (?P<tag>[%(tag_types)s]?)  \\s* (?P<tag_key>[\\s\\S]+?)\n        )\n        \\s* %(ctag)s\n    "
        % {
            "tag_types": tag_types,
            "otag": re.escape(delimiters[0]),
            "ctag": re.escape(delimiters[1]),
        }
    )
    return re.compile(tag, re.VERBOSE)


class ParsingError(Exception):
    pass


def _format(obj, exclude=None):
    if exclude is None:
        exclude = []
    exclude.append("key")
    attrs = obj.__dict__
    names = list(set(attrs.keys()) - set(exclude))
    names.sort()
    names.insert(0, "key")
    args = ["%s=%s" % (name, repr(attrs[name])) for name in names]
    return "%s(%s)" % (obj.__class__.__name__, (", ").join(args))


class _CommentNode(object):
    def __repr__(self):
        return _format(self)

    def render(self, engine, context):
        return ""


class _ChangeNode(object):
    def __init__(self, delimiters):
        self.delimiters = delimiters

    def __repr__(self):
        return _format(self)

    def render(self, engine, context):
        return ""


class _EscapeNode(object):
    def __init__(self, key):
        self.key = key

    def __repr__(self):
        return _format(self)

    def render(self, engine, context):
        s = engine.fetch_string(context, self.key)
        return engine.escape(s)


class _LiteralNode(object):
    def __init__(self, key):
        self.key = key

    def __repr__(self):
        return _format(self)

    def render(self, engine, context):
        s = engine.fetch_string(context, self.key)
        return engine.literal(s)


class _PartialNode(object):
    def __init__(self, key, indent):
        self.key = key
        self.indent = indent

    def __repr__(self):
        return _format(self)

    def render(self, engine, context):
        template = engine.resolve_partial(self.key)
        template = re.sub(NON_BLANK_RE, self.indent + "\\1", template)
        return engine.render(template, context)


class _InvertedNode(object):
    def __init__(self, key, parsed_section):
        self.key = key
        self.parsed_section = parsed_section

    def __repr__(self):
        return _format(self)

    def render(self, engine, context):
        data = engine.resolve_context(context, self.key)
        if data:
            return ""
        else:
            return self.parsed_section.render(engine, context)


class _SectionNode(object):
    def __init__(self, key, parsed, delimiters, template, index_begin, index_end):
        self.delimiters = delimiters
        self.key = key
        self.parsed = parsed
        self.template = template
        self.index_begin = index_begin
        self.index_end = index_end

    def __repr__(self):
        return _format(self, exclude=["delimiters", "template"])

    def render(self, engine, context):
        values = engine.fetch_section_data(context, self.key)
        parts = []
        for val in values:
            if callable(val):
                val = val(self.template[self.index_begin : self.index_end])
                val = engine._render_value(val, context, delimiters=self.delimiters)
                parts.append(val)
                continue
                context.push(val)
                parts.append(self.parsed.render(engine, context))
                context.pop()

        return str(("").join(parts))


class _Parser(object):
    _delimiters = None
    _template_re = None

    def __init__(self, delimiters=None):
        if delimiters is None:
            delimiters = defaults.DELIMITERS
        self._delimiters = delimiters

    def _compile_delimiters(self):
        self._template_re = _compile_template_re(self._delimiters)

    def _change_delimiters(self, delimiters):
        self._delimiters = delimiters
        self._compile_delimiters()

    def parse(self, template):
        """
        Parse a template string starting at some index.
        
        This method uses the current tag delimiter.
        
        Arguments:
        
          template: a unicode string that is the template to parse.
        
          index: the index at which to start parsing.
        
        Returns:
        
          a ParsedTemplate instance.
        
        """
        self._compile_delimiters()
        start_index = 0
        content_end_index, parsed_section, section_key = (None, None, None)
        parsed_template = ParsedTemplate()
        states = []
        while 1:
            match = self._template_re.search(template, start_index)
            if match is None:
                break
            match_index = match.start()
            end_index = match.end()
            matches = match.groupdict()
            if matches["change"] is not None:
                matches.update(tag="=", tag_key=matches["delims"])
            else:
                if matches["raw"] is not None:
                    matches.update(tag="&", tag_key=matches["raw_name"])
            tag_type = matches["tag"]
            tag_key = matches["tag_key"]
            leading_whitespace = matches["whitespace"]
            did_tag_begin_line = (
                match_index == 0 or template[match_index - 1] in END_OF_LINE_CHARACTERS
            )
            did_tag_end_line = (
                end_index == len(template)
                or template[end_index] in END_OF_LINE_CHARACTERS
            )
            is_tag_interpolating = tag_type in ("", "&")
            if did_tag_begin_line and did_tag_end_line and not is_tag_interpolating:
                if end_index < len(template):
                    end_index += template[end_index] == "\r" and 1 or 0
                if end_index < len(template):
                    end_index += template[end_index] == "\n" and 1 or 0
                else:
                    if leading_whitespace:
                        match_index += len(leading_whitespace)
                        leading_whitespace = ""
                if start_index != match_index:
                    parsed_template.add(template[start_index:match_index])
                start_index = end_index
                if tag_type in ("#", "^"):
                    state = (tag_type, end_index, section_key, parsed_template)
                    states.append(state)
                    section_key, parsed_template = tag_key, ParsedTemplate()
                    continue
                    if tag_type == "/":
                        if tag_key != section_key:
                            raise ParsingError(
                                "Section end tag mismatch: %s != %s"
                                % (tag_key, section_key)
                            )
                        parsed_section = parsed_template
                        tag_type, section_start_index, section_key, parsed_template = (
                            states.pop()
                        )
                        node = self._make_section_node(
                            template,
                            tag_type,
                            tag_key,
                            parsed_section,
                            section_start_index,
                            match_index,
                        )
                    else:
                        node = self._make_interpolation_node(
                            tag_type, tag_key, leading_whitespace
                        )
                    parsed_template.add(node)

        if start_index != len(template):
            parsed_template.add(template[start_index:])
        return parsed_template

    def _make_interpolation_node(self, tag_type, tag_key, leading_whitespace):
        """
        Create and return a non-section node for the parse tree.
        
        """
        if tag_type == "!":
            return _CommentNode()
        if tag_type == "=":
            delimiters = tag_key.split()
            self._change_delimiters(delimiters)
            return _ChangeNode(delimiters)
        if tag_type == "":
            return _EscapeNode(tag_key)
        if tag_type == "&":
            return _LiteralNode(tag_key)
        if tag_type == ">":
            return _PartialNode(tag_key, leading_whitespace)
        raise Exception("Invalid symbol for interpolation tag: %s" % repr(tag_type))

    def _make_section_node(
        self,
        template,
        tag_type,
        tag_key,
        parsed_section,
        section_start_index,
        section_end_index,
    ):
        """
        Create and return a section node for the parse tree.
        
        """
        if tag_type == "#":
            return _SectionNode(
                tag_key,
                parsed_section,
                self._delimiters,
                template,
                section_start_index,
                section_end_index,
            )
        if tag_type == "^":
            return _InvertedNode(tag_key, parsed_section)
        raise Exception("Invalid symbol for section tag: %s" % repr(tag_type))
